package com.niiwoo.civet.account.service.local;

import com.alibaba.fastjson.JSON;
import com.niiwoo.civet.account.dao.entity.AccountBase;
import com.niiwoo.civet.account.dao.entity.MoneyTotal;
import com.niiwoo.civet.account.dao.entity.MoneyTotalRecord;
import com.niiwoo.civet.account.dao.mapper.AccountBaseMapperExt;
import com.niiwoo.civet.account.dao.mapper.MoneyTotalMapperExt;
import com.niiwoo.civet.account.dao.mapper.MoneyTotalRecordMapper;
import com.niiwoo.civet.account.dto.common.MoneyTotalDTO;
import com.niiwoo.civet.account.dto.response.QueryMoneyTotalResponseDTO;
import com.niiwoo.civet.account.enums.AccountAmountTypeEnum;
import com.niiwoo.tripod.lanmao.enums.UserRoleEnum;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * 金额统计服务
 * 
 * @author liaojunjun
 *
 */
@org.springframework.stereotype.Service
public class MoneyTotalLocalService {
	
	private static final Logger logger = LoggerFactory.getLogger(MoneyTotalLocalService.class);

	@Autowired
	private MoneyTotalMapperExt moneyTotalMapperExt;

	@Autowired
	private MoneyTotalRecordMapper moneyTotalRecordMapper;

	@Autowired
	private SnowflakeIdWorker snowflakeIdWorker;

	@Autowired
	private AccountService accountService;

	@Autowired
	private AccountBaseMapperExt accountBaseMapperExt;

	/**
	 * 用户金额统计列表
	 * 
	 * @param userId
	 * @return
	 */
	public List<MoneyTotal> getUserMoneyTotal(String userId) {

		return moneyTotalMapperExt.queryMoneyTotalByUserId(userId,null);
	}

	/**
	 * 累计财富计算
	 * 
	 * @param dtoList
	 */
	@Transactional
	public void moneyTotal(List<MoneyTotalDTO> dtoList) {
        logger.info("累计财富计算 dtoList.size():{}", dtoList.size());
        if(!CollectionUtils.isEmpty(dtoList)){
			Comparator<MoneyTotalDTO> accountIdSort = (MoneyTotalDTO t1, MoneyTotalDTO t2) -> (t1.getAccountId().compareTo(t2.getAccountId()));
			Collections.sort(dtoList, accountIdSort);
			for (MoneyTotalDTO moneyTotalDTO : dtoList) {
				saveMoneyTotal(moneyTotalDTO);
			}
		}
	}

	/**
	 * 累计财富计算
	 * 
	 * @param dto
	 */
	@Transactional
	public void moneyTotal(MoneyTotalDTO dto) {
		saveMoneyTotal(dto);
	}

	private void saveMoneyTotal(MoneyTotalDTO dto) {
		logger.info("saveMoneyTotal MoneyTotalDTO:{}", JSON.toJSON(dto));
		this.moneyTotalParamCheck(dto);

		Long accountId = dto.getAccountId();
		AccountBase accountBase = accountService.loadAccountById(accountId);
		Assert.notNull(accountBase.getIsShowBill(), "accountBase isShowBill is null. accountId="+accountId);

		dto.setUserId(accountBase.getUserId());
		if ( accountBase.getIsShowBill().equals((byte)1) ){
			MoneyTotal moneyTotal = moneyTotalMapperExt.queryMoneyTotalByAccountIdAndAmountType(accountId, dto.getAmountType().getCode());
			if (moneyTotal != null) {
				moneyTotal = moneyTotalMapperExt.selectByPrimaryKeyForUpdate(moneyTotal.getId());
			}
			if (moneyTotal == null) {
				moneyTotal = new MoneyTotal();
				moneyTotal.setUserId(dto.getUserId());
				moneyTotal.setAccountId(accountId);
				moneyTotal.setAmountType(dto.getAmountType().getCode());
				moneyTotal.setAmountTypeDesc(dto.getAmountType().getDesc());
				moneyTotal.setAmount(dto.getAmount());
				moneyTotal.setCreatedTime(new Date());
				moneyTotal.setUpdatedTime(new Date());

				moneyTotal.setId(snowflakeIdWorker.nextId());
				moneyTotalMapperExt.insertSelective(moneyTotal);
			} else {
				MoneyTotal record = new MoneyTotal();
				record.setId(moneyTotal.getId());
				record.setAmount(moneyTotal.getAmount().add(dto.getAmount()));

				moneyTotalMapperExt.updateByPrimaryKeySelective(record);
			}

			//投资奖励记录的同时要记录到平台奖励中 查询所有收益时不需要累加投资奖励
			if(AccountAmountTypeEnum.INVEST_GIVEN.getCode().equals(dto.getAmountType().getCode())){
				//平台奖励
				moneyTotal = moneyTotalMapperExt.queryMoneyTotalByAccountIdAndAmountType(accountId, AccountAmountTypeEnum.PLATFORM_GIVEN.getCode());
				if (moneyTotal != null) {
					moneyTotal = moneyTotalMapperExt.selectByPrimaryKeyForUpdate(moneyTotal.getId());
				}
				if (moneyTotal == null) {
					moneyTotal = new MoneyTotal();
					moneyTotal.setUserId(dto.getUserId());
					moneyTotal.setAccountId(accountId);
					moneyTotal.setAmountType(AccountAmountTypeEnum.PLATFORM_GIVEN.getCode());
					moneyTotal.setAmountTypeDesc(AccountAmountTypeEnum.PLATFORM_GIVEN.getDesc());
					moneyTotal.setAmount(dto.getAmount());
					moneyTotal.setCreatedTime(new Date());
					moneyTotal.setUpdatedTime(new Date());

					moneyTotal.setId(snowflakeIdWorker.nextId());
					moneyTotalMapperExt.insertSelective(moneyTotal);
				} else {
					MoneyTotal record = new MoneyTotal();
					record.setId(moneyTotal.getId());
					record.setAmount(moneyTotal.getAmount().add(dto.getAmount()));

					moneyTotalMapperExt.updateByPrimaryKeySelective(record);
				}
			}

			MoneyTotalRecord moneyTotalRecord = new MoneyTotalRecord();
			moneyTotalRecord.setUserId(dto.getUserId());
			moneyTotalRecord.setAccountId(accountId);
			moneyTotalRecord.setAmountType(dto.getAmountType().getCode());
			moneyTotalRecord.setAmountTypeDesc(dto.getAmountType().getDesc());
			moneyTotalRecord.setAmount(dto.getAmount());
			moneyTotalRecord.setCreatedTime(new Date());
			moneyTotalRecord.setUpdatedTime(new Date());

			moneyTotalRecord.setId(snowflakeIdWorker.nextId());
			moneyTotalRecordMapper.insertSelective(moneyTotalRecord);
		}
	}

	/**
	 * 查询用户财富累计
	 * 
	 * @param userId
	 * @return
	 */
	public QueryMoneyTotalResponseDTO queryMoneyTotal(String userId,Long accountId) {
		Assert.notNull(userId, "queryMoneyTotal userId is null.");

		QueryMoneyTotalResponseDTO result = new QueryMoneyTotalResponseDTO(BigDecimal.ZERO, BigDecimal.ZERO,
				BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO,
				BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO,
				BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO,
				BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO,
				BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO,
				BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO,
				BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO);

		List<MoneyTotal> list = moneyTotalMapperExt.queryMoneyTotalByUserId(userId,accountId);
		if (list != null) {
			for (MoneyTotal moneyTotal : list) {
				if (moneyTotal != null) {
					AccountAmountTypeEnum amountType = AccountAmountTypeEnum.enumOf(moneyTotal.getAmountType());
					BigDecimal amount = moneyTotal.getAmount();
					switch (amountType) {
					case INVEST_INCOME:
						result.setInvestIncome(result.getInvestIncome().add(amount));
						break;
					case RESEARCH_INCOME:
						result.setResearchIncome(result.getResearchIncome().add(amount));
						break;
					case GUARANTEE_INCOME:
						result.setGuaranteeIncome(result.getGuaranteeIncome().add(amount));
						break;
					case PLATFORM_GIVEN:
						result.setPlatformGiven(result.getPlatformGiven().add(amount));
						break;
					case INVEST_GIVEN:
						result.setInvestGiven(result.getInvestGiven().add(amount));
						break;
					case PLATFORM_PROVIDE:
						result.setPlatformProvide(result.getPlatformProvide().add(amount));
						break;
					case USER_RED_PACKET:
						result.setUserRedPacket(result.getUserRedPacket().add(amount));
						break;

					case INVEST_FEE:
						result.setInvestFee(result.getInvestFee().add(amount));
						break;
					case GUARANTEE_INCOME_FEE:
						result.setGuaranteeIncomeFee(result.getGuaranteeIncomeFee().add(amount));
						break;
					case WITHDRAW_FEE:
						result.setWithdrawFee(result.getWithdrawFee().add(amount));
						break;
					case CREDITOR_SALE_FEE:
						result.setCreditorSaleFee(result.getCreditorSaleFee().add(amount));
						break;
					case BORROW_GUARANTEE_SERVICE_FEE:
						result.setBorrowGuaranteeServiceFee(result.getBorrowGuaranteeServiceFee().add(amount));
						break;
					case BORROW_PLATFORM_SERVICE_FEE:
						result.setBorrowPlatformServiceFee(result.getBorrowPlatformServiceFee().add(amount));
						break;
					case BORROW_PLATFORM_MANAGER_FEE:
						result.setBorrowPlatformManagerFee(result.getBorrowPlatformManagerFee().add(amount));
						break;
					case BORROW_INTEREST:
						result.setBorrowInterest(result.getBorrowInterest().add(amount));
						break;
					case BORROW_PENALTY_FEE:
						result.setBorrowPenaltyFee(result.getBorrowPenaltyFee().add(amount));
						break;
					case BORROW_GUARANTEE_FEE:
						result.setBorrowGuaranteeFee(result.getBorrowGuaranteeFee().add(amount));
						break;
					case COLLECTION_SERVICE_FEE:
						result.setCollectionServiceFee(result.getCollectionServiceFee().add(amount));
						break;
					case INVESTIGATE_FEE:
						result.setInvestigateFee(result.getInvestigateFee().add(amount));
						break;

					case RECHARGE_TOTAL:
						result.setRechargeTotal(result.getRechargeTotal().add(amount));
						break;
					case WITHDRAW_TOTAL:
						result.setWithdrawTotal(result.getWithdrawTotal().add(amount));
						break;
					case INVEST_TOTAL:
						result.setInvestTotal(result.getInvestTotal().add(amount));
						break;
					case BORROW_TOTAL:
						result.setBorrowTotal(result.getBorrowTotal().add(amount));
						break;
					case REPAYMENT_TOTAL:
						result.setRepaymentTotal(result.getRepaymentTotal().add(amount));
						break;

					case DUE_IN_CORPUS:
						result.setDueInCorpus(result.getDueInCorpus().add(amount));
						break;
					case DUE_IN_INTEREST:
						result.setDueInInterest(result.getDueInInterest().add(amount));
						break;
					case DUE_IN_PENALTY_FEE:
						result.setDueInPenaltyFee(result.getDueInPenaltyFee().add(amount));
						break;
					case GUARANTEE_CASH_DEPOSIT:
						result.setGuaranteeCashDeposit(result.getGuaranteeCashDeposit().add(amount));
						break;
					case ADVANCE:
						result.setAdvance(result.getAdvance().add(amount));
						break;

					case INVEST_FREEZE:
						result.setInvestFreeze(result.getInvestFreeze().add(amount));
						break;
					case GUARANTEE_FREEZE:
						result.setGuaranteeFreeze(result.getGuaranteeFreeze().add(amount));
						break;
					case REPAYMENT_FREEZE:
						result.setRepaymentFreeze(result.getRepaymentFreeze().add(amount));
						break;
					case WITHDRAW_FREEZE:
						result.setWithdrawFreeze(result.getWithdrawFreeze().add(amount));
						break;

					default:
						break;
					}
				}
			}

			if(accountId == null ){
				AccountBase borrowAccount = accountService.getNiiwooAccount(userId, UserRoleEnum.BORROWERS);
				AccountBase investorAccount = accountService.getNiiwooAccount(userId, UserRoleEnum.INVESTOR);
				result.setBorrowerAvailableBalance((borrowAccount != null ? borrowAccount.getAvailableAmount(): BigDecimal.ZERO));
				result.setInvestorAvailableBalance((investorAccount != null ? investorAccount.getAvailableAmount() : BigDecimal.ZERO));
			}else {
				AccountBase orgAccount = accountBaseMapperExt.selectByPrimaryKey(accountId);
				if(orgAccount == null){
					result.setBorrowerAvailableBalance(BigDecimal.ZERO);
					result.setInvestorAvailableBalance(BigDecimal.ZERO);
				}else{
					Boolean roleFlag = UserRoleEnum.BORROWERS.getValue().equals(orgAccount.getAccountRole());
					result.setBorrowerAvailableBalance(roleFlag ? orgAccount.getAvailableAmount() : BigDecimal.ZERO);
					result.setInvestorAvailableBalance(roleFlag ? BigDecimal.ZERO : orgAccount.getAvailableAmount());
				}
			}

			// 待收本息(待收本金+待收利息)
			result.setDueInCorpusInterest(result.getDueInCorpus().add(result.getDueInInterest()));
			// 其它金额 (投资冻结+担保中未成标金额+还款冻结+提现冻结)
			result.setOtherAmount(result.getInvestFreeze().add(result.getGuaranteeFreeze())
					.add(result.getRepaymentFreeze().add(result.getWithdrawFreeze())));
			// 在途金额(担保保证金+垫付金额+其它金额)-- 5.5.0取消待收本息
			result.setOnWayAmount(result.getGuaranteeCashDeposit()
					.add(result.getAdvance()).add(result.getOtherAmount()));
			// 财富总额(可用余额[投资账户余额+借款账户余额]+在途资金+待收本息)
			result.setAmountTotal(result.getBorrowerAvailableBalance().add(result.getInvestorAvailableBalance())
					.add(result.getOnWayAmount()).add(result.getDueInCorpusInterest()));
			
			// 总收益(投资收益+尽调收益+担保收益+平台奖励+平台发放+用户红包) 投资奖励不能加 投资奖励已经包含在平台奖励中
			result.setIncomeTotal(result.getInvestIncome().add(result.getResearchIncome())
					.add(result.getGuaranteeIncome()).add(result.getPlatformGiven()).add(result.getPlatformProvide())
					.add(result.getUserRedPacket()));
			
			// 借款费用(借款的所有支出费用：担保费+预仲裁+平台服务费+平台管理费+利息+罚息)
			result.setBorrowFee(result.getBorrowGuaranteeServiceFee().add(result.getBorrowPlatformServiceFee()).add(result.getBorrowPlatformManagerFee())
					.add(result.getBorrowInterest()).add(result.getBorrowPenaltyFee()).add(result.getBorrowGuaranteeFee()).add(result.getCollectionServiceFee()).add(result.getInvestigateFee()));
			// 总支出(投资费用+担保收益支出费用+提现手续费+借款费用)
			result.setPayTotal(result.getInvestFee().add(result.getGuaranteeIncomeFee()).add(result.getWithdrawFee())
					.add(result.getCreditorSaleFee()).add(result.getBorrowFee()));
		}
		
		/**
		 * 是否存在财富数据 前端展示使用
		 * 前端展示规则，以下任意一项数据不为0，前端展示相应数据：
			1.投资账户可用余额
			2.借款账户可用余额
			3.累计收益
			4.费用支出
			5.累计充值金额
			6.累计提现金额
		 */
		boolean isExistMoneyData = result.getBorrowerAvailableBalance().compareTo(BigDecimal.ZERO) != 0
				|| result.getInvestorAvailableBalance().compareTo(BigDecimal.ZERO) != 0
				|| result.getIncomeTotal().compareTo(BigDecimal.ZERO) != 0
				|| result.getPayTotal().compareTo(BigDecimal.ZERO) != 0
				|| result.getRechargeTotal().compareTo(BigDecimal.ZERO) != 0
				|| result.getWithdrawTotal().compareTo(BigDecimal.ZERO) != 0;
		
		result.setIsExistMoneyData(isExistMoneyData);
		logger.info("queryMoneyTotal userId:{}; result:{}", userId, JSON.toJSON(result));
		return result;
	}

	private void moneyTotalParamCheck(MoneyTotalDTO dto) {
		Assert.notNull(dto, "moneyTotal DTO is null.");
		// Assert.notNull(dto.getUserId(), "moneyTotal userId is null.");
		Assert.notNull(dto.getAccountId(), "moneyTotal accountId is null.");
		Assert.notNull(dto.getAmountType(), "moneyTotal amountType is null.");
		Assert.notNull(dto.getAmount(), "moneyTotal amount is null.");
	}

	/**
	 * 根据类型统计总金额
	 * @param amountTypeEnumList
	 * @return
	 */
	public BigDecimal queryMoneyTotalByAmountTypeList(AccountAmountTypeEnum ... amountTypeEnumList){
		return moneyTotalMapperExt.queryMoneyTotalByAmountTypeList(Arrays.asList(amountTypeEnumList));
	}

	public BigDecimal queryMoneyByUserIdAndAccountType(String userId, AccountAmountTypeEnum amountType){
		return moneyTotalMapperExt.queryMoneyByUserIdAndAccountType(userId,amountType.getCode());
	}
}
